#include <CType.h>
#include <MyLib.h>

#define GDT_SEG16               (0x0000)
#define GDT_OFFSET16            (0x8000)
#define GDTR_OFFSET16           (0x7FE2)

#define GDT_OFFSET32            (0x00008000)

#define GDT_SEG_COUNT           (32)

#define SEG_ATTR_TYPE_W         (0x02)
#define SEG_ATTR_TYPE_S         (0x04)
#define SEG_ATTR_TYPE_X         (0x08)

#define SEG_ATTR_EXIST          (0x80)
#define SEG_ATTR_CODEDATASEG    (0x10)

#define SEG_ATTR_SEG32          (0x40)
#define SEG_ATTR_4K             (0x80)

#define DEFAULT_SEG_LENGTH      (0xFFFF)
#define SEG_LENGTH_HIGH_BYTE    (0x0F)

#define DEFAULT_SEG_SS_LENGTH   (0x0120)
#define SEG_SS_LENGTH_HIGH_BYTE (0x00)

#define SEG_32MODE_BASE_L       (0x0000)
#define SEG_32MODE_BASE_HL      (0x00)
#define SEG_32MODE_BASE_HH      (0x00)

/* a exist segment with write and exist flag */

#define SEG_ATTR1_DS            (SEG_ATTR_CODEDATASEG | SEG_ATTR_EXIST | SEG_ATTR_TYPE_W)
#define SEG_ATTR2_DS_4G         (SEG_ATTR_SEG32 | SEG_ATTR_4K | SEG_LENGTH_HIGH_BYTE)

#define SEG_ATTR1_CS            (SEG_ATTR_CODEDATASEG | SEG_ATTR_EXIST | SEG_ATTR_TYPE_X)
#define SEG_ATTR2_CS_4G         (SEG_ATTR_SEG32 | SEG_ATTR_4K | SEG_LENGTH_HIGH_BYTE)

#define SEG_ATTR1_SS            (SEG_ATTR_CODEDATASEG | SEG_ATTR_EXIST | SEG_ATTR_TYPE_S | SEG_ATTR_TYPE_W)
#define SEG_ATTR2_SS_4G         (SEG_ATTR_SEG32 | SEG_ATTR_4K | SEG_SS_LENGTH_HIGH_BYTE)

typedef struct  {
    int16_t SegLength;
    int16_t SegBaseLow;
    int8_t SegBaseHighL;
    int8_t SegAtt1;
    int8_t SegAtt2;
    int8_t SegBaseHighH;
} SegDescriptor;

static void * GetGdtTableBase()
{
    asm mov dx, GDT_SEG16;
    asm mov ax, GDT_OFFSET16
}

static void * GetGdtrBase()
{
    asm mov dx, GDT_SEG16;
    asm mov ax, GDTR_OFFSET16;
}

extern void Goto32();

extern void get_mem_info();
/*
  Flat memory module

  kernel run in a flat memory space, base address is 0x0, segment size is 4G

  0x00000000 - 0x00007BFF unused
  0x00007C00 - 0x00007DFF boot sector
  0x00007E00 - 0x00007FFF temp space for load GDTR
  0x00008000 - 0x000080FF GDT table(temp space for boot sector)
  0x00008100 - 0x00008BFF unused
  0x00008C00 - 0x00009DFF FAT space for boot sector
  0x00009E00 - 0x0000FFFF unused
  0x00010000 - 0x0001FFFF run time space for bootloader
  0x00020000 - 0x0009FFFF kernel file space with text, data, bss, rdata
  0x000A0000 - 0x000FFFFF BIOS space
  0x00100000 - 0x001007FF interrupt table
  0x00100800 - 0x0011FFFF interrupt handler
  0x00120000 - 0x001FFFFF kernel stack
  0x00200000 - 0xFFFFFFFF unused

  global segment:
  index     type    base        limit       used for
  0x0008    data    0x00000000  0xFFFFFFFF  ds, es, fs, gs
  0x0010    code    0x00000000  0xFFFFFFFF  cs
  0x0018    stack   0x00000000  0xFFFFFFFF  ss
 */

void Prepare32()
{
    SegDescriptor * gdtBase;
    int8_t * pGdtr;

    gdtBase = (SegDescriptor *)GetGdtTableBase();
    memset(gdtBase, 0, sizeof(SegDescriptor) * (GDT_SEG_COUNT));

    /******************************/

    /* Seg 0008 Base 00000000 */

    gdtBase[1].SegBaseLow = SEG_32MODE_BASE_L;
    gdtBase[1].SegBaseHighH = SEG_32MODE_BASE_HH;
    gdtBase[1].SegBaseHighL = SEG_32MODE_BASE_HL;

    /* Seg Length FFFFF */

    gdtBase[1].SegLength = DEFAULT_SEG_LENGTH;

    /* Attribute with writeable 4G segment */

    gdtBase[1].SegAtt1 = SEG_ATTR1_DS;
    gdtBase[1].SegAtt2 = SEG_ATTR2_DS_4G;

    /******************************/

    /* Seg 0010 Base 00000000 */

    gdtBase[2].SegBaseLow = SEG_32MODE_BASE_L;
    gdtBase[2].SegBaseHighL = SEG_32MODE_BASE_HH;
    gdtBase[2].SegBaseHighH = SEG_32MODE_BASE_HL;

    /* Seg Length FFFFF */

    gdtBase[2].SegLength = DEFAULT_SEG_LENGTH;

    /* Attribute with executable 4G segment */

    gdtBase[2].SegAtt1 = SEG_ATTR1_CS;
    gdtBase[2].SegAtt2 = SEG_ATTR2_CS_4G;

    /******************************/

    /* Seg 0018 Base 00000000 */

    gdtBase[3].SegBaseLow = SEG_32MODE_BASE_L;
    gdtBase[3].SegBaseHighL = SEG_32MODE_BASE_HH;
    gdtBase[3].SegBaseHighH = SEG_32MODE_BASE_HL;

    /* Seg Length FFFFF */

    gdtBase[3].SegLength = DEFAULT_SEG_SS_LENGTH;

    /* Attribute with writeable stack segment */

    gdtBase[3].SegAtt1 = SEG_ATTR1_SS;
    gdtBase[3].SegAtt2 = SEG_ATTR2_SS_4G;

    pGdtr = GetGdtrBase();

    *((int16_t *)pGdtr) = sizeof(SegDescriptor) * (GDT_SEG_COUNT) - 1;

    *((int32_t *)(pGdtr + sizeof(int16_t))) = GDT_OFFSET32;

    get_mem_info();

    Goto32();
}
